#include <atomic>
#include <chrono>
#include <cstring>
#include <immintrin.h>
#include <string>
#include <thread>
#include <vector>
#include "gtest/gtest.h"
#include "lock_free_ring_buffer.h"

#define UNUSED(x) (void)(x)

namespace Base {
namespace SystemLog {
namespace {
constexpr size_t TEST_MESSAGE_SIZE = 128;
constexpr int32_t TEST_THREADS = 16;
constexpr int32_t TEST_THREAD_PERFORMANCE = 100000;
const char TEST_CHAR = 't';

class PerfTimer {
public:
    PerfTimer() : start_(std::chrono::high_resolution_clock::now()) {}
    
    double elapsed() const {
        auto end = std::chrono::high_resolution_clock::now();
        return std::chrono::duration<double>(end - start_).count();
    }
    
private:
    std::chrono::time_point<std::chrono::high_resolution_clock> start_;
};
} // namespace

class LockFreeRingBufferPerformanceTest : public testing::Test {
public:
    static void SetUpTestCase(void) {}
    static void TearDownTestCase(void) {}
    void SetUp() {}
    void TearDown() {}
};

/**
 * @tc.name: LockFreeRingBufferPerformanceTest_001
 * @tc.desc: Multi threaded performance testing
 * @tc.type: PERF
 */
TEST_F(LockFreeRingBufferPerformanceTest, LockFreeRingBufferPerformanceTest_001)
{
    LockFreeRingBuffer<> buffer;
    std::atomic<bool> start{false};
    std::atomic<int> readyCount{0};
    std::atomic<size_t> successCount{0};
    std::atomic<size_t> failCount{0};
    std::string message(TEST_MESSAGE_SIZE, TEST_CHAR);

    auto worker = [&](int iterations) {
        while (!start) {
            _mm_pause(); // 低功耗等待
        }
        for (int j = 0; j < iterations; ++j) {
            auto errCode = buffer.Append(message.c_str(), message.size());
            if (errCode == ERR_OK) {
                successCount.fetch_add(1, std::memory_order_relaxed);
            } else {
                failCount.fetch_add(1, std::memory_order_relaxed);
            }
        }
    };

    size_t collectedSize = 0;
    auto consumer = [&](const char* data, size_t len) -> ErrCode {
        collectedSize += len;
        for (size_t i = 0; i < len; i += TEST_MESSAGE_SIZE) {
            size_t chunk = std::min(TEST_MESSAGE_SIZE, len - i);
            EXPECT_EQ(0, memcmp(data + i, message.data(), chunk));
        }
        return ERR_OK;
    };
    
    std::vector<std::thread> threads;
    for (int i = 0; i < TEST_THREADS; ++i) {
        threads.emplace_back(worker, TEST_THREAD_PERFORMANCE);
    }
    std::this_thread::sleep_for(std::chrono::milliseconds(10));

    PerfTimer timer;
    start = true;
    for (auto& t : threads) {
        t.join();
    }
    double elapsedTime = timer.elapsed();
    buffer.Collect(consumer);
    
    size_t totalMsgs = TEST_THREADS * TEST_THREAD_PERFORMANCE;
    size_t totalBytes = totalMsgs * TEST_MESSAGE_SIZE;
    constexpr double TEST_MB = 1024.0 * 1024.0;
    
    GTEST_LOG_(INFO) << "Total messages: " << totalMsgs << "\n";
    GTEST_LOG_(INFO) << "Success: " << successCount << " | Failed: " << failCount << "\n";
    GTEST_LOG_(INFO) << "Total bytes: " << totalBytes << " bytes\n";
    GTEST_LOG_(INFO) << "Elapsed time: " << elapsedTime << " seconds\n";
    GTEST_LOG_(INFO) << "Throughput: " << static_cast<double>(totalMsgs) / elapsedTime << " msg/sec\n";
    GTEST_LOG_(INFO) << "Bandwidth: " << (static_cast<double>(totalBytes) / TEST_MB) / elapsedTime << " MB/sec\n";

    EXPECT_EQ(collectedSize, totalBytes);
}

} // namespace SystemLog
} // namespace Base
